#include <node.h>
#include <v8.h>
#include "AudioManager.h"
#include "LogUtil.h"
#include "macro.h"
#include "math.h"

using namespace v8;

AudioManager* manager = NULL;
// 当前选中的目标
int trackId = -1;
Track * track = nullptr;
int cellId = -1;
int audioId = -1;
Audio * audio = nullptr;


void selectCell(int id) {
	if (id == cellId) {
		return;
	}
	cellId = track && track->isCellValid(id) ? id : -1;
}

void unselectCell() {
	cellId = -1;
}

void selectTrack(int id) {
	if (id == trackId) {
		return;
	}
	unselectCell();
	track = manager ? manager->getTrack(id) : nullptr;
	trackId = track ? id : -1;
}

void unselectTrack() {
	trackId = -1;
	track = nullptr;
}

void selectAudio(int id) {
	if (id == audioId) {
		return;
	}
	audio = manager ? manager->getAudio(id) : nullptr;
	audioId = audio ? id : -1;
}

void unselectAudio() {
	audioId = -1;
	audio = nullptr;
}


void Method_Init(const FunctionCallbackInfo<Value>& args){
	if(manager != NULL){
		manager->releaseInstance();
	}
	manager = AudioManager::getInstance();
	LogUtil::getInstance()->log("fmod","inited");
}

void Method_Destroy(const FunctionCallbackInfo<Value>& args){
	if(manager != NULL){
		manager->releaseInstance();
	}
	manager = NULL;
	unselectTrack();
	unselectCell();
	unselectAudio();
}

void Method_Select_Audio(const FunctionCallbackInfo<Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (args.Length() == 1 && args[0]->IsInt32()) {
		selectAudio(args[0]->Int32Value());
	} else {
		unselectAudio();
	}
	args.GetReturnValue().Set(Int32::New(isolate, audioId));
}

void Method_Load_Audio(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (manager != nullptr && args.Length() == 1 && args[0]->IsString()) {
		v8::String::Utf8Value str(args[0]->ToString());
		audioId = manager->load(*str);
		audio = manager->getAudio(audioId);
	} else {
		unselectAudio();
	}
	args.GetReturnValue().Set(Int32::New(isolate, audioId));
}

void Method_Unload_Audio(const FunctionCallbackInfo<Value>& args){
	if(!manager){
		return;
	}
	
	manager->release(audioId);
	unselectAudio();
}

void Method_Update(const FunctionCallbackInfo<Value>& args){
	if (manager == nullptr) {
		return;
	}

	manager->update();
}

void Method_Play_Audio(const FunctionCallbackInfo<Value>& args){
	if (manager == nullptr || audio == nullptr) {
		return;
	}
	manager->prepare(audioId);
	audio->play();
}

void Method_Pause_Audio(const FunctionCallbackInfo<Value>& args){
	if (audio != nullptr) {
		audio->pause();
	}
}

void Method_Stop_Audio(const FunctionCallbackInfo<Value>& args){
	if (audio != nullptr) {
		audio->stop();
	}
}

void Method_Toggle_Audio(const FunctionCallbackInfo<Value>& args){
	if (audio != nullptr) {
		audio->toggle();
	}
}

void Method_Volume_Audio(const FunctionCallbackInfo<Value>& args){
	if (audio != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		audio->setVolume(static_cast<float>(args[0]->NumberValue() / 100.0));
	}
}

void Method_Seek_Audio(const FunctionCallbackInfo<Value>& args){
	if (audio != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		audio->seek(args[1]->NumberValue());
	}
}

void Method_Time_Audio(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double time;
	if (audio != nullptr) {
		time = audio->getTime();
	} else {
		time = NAN;
	}
	args.GetReturnValue().Set(Number::New(isolate, time));
}

void Method_Length_Audio(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double len;
	if (audio != nullptr) {
		len = audio->getLength();
	} else {
		len = NAN;
	}
	args.GetReturnValue().Set(Number::New(isolate, len));
}

void Method_Select_Cell(const FunctionCallbackInfo<Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (args.Length() == 1 && args[0]->IsInt32()) {
		selectCell(args[0]->Int32Value());
	} else {
		unselectCell();
	}
	args.GetReturnValue().Set(Int32::New(isolate, cellId));
}

void Method_Add_Cell(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (track != nullptr && audio != nullptr) {
		cellId = track->addCell(audio);
	} else {
		unselectCell();
	}
	args.GetReturnValue().Set(Int32::New(isolate, cellId));
}

void Method_Remove_Cell(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && cellId >= 0) {
		track->removeCell(cellId);
	}
}

void Method_Offset_Cell(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && cellId >= 0 && args.Length() == 1 && args[0]->IsNumber()) {
		track->setCellOffset(cellId, args[0]->NumberValue());
	}
}

void Method_Select_Track(const FunctionCallbackInfo<Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (args.Length() == 1 && args[0]->IsInt32()) {
		selectTrack(args[0]->Int32Value());
	} else {
		unselectTrack();
	}
	args.GetReturnValue().Set(Int32::New(isolate, cellId));
}

void Method_Create_Track(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	if (manager != nullptr) {
		trackId = manager->createTrack();
		track = manager->getTrack(trackId);
	} else {
		unselectTrack();
	}
	args.GetReturnValue().Set(Int32::New(isolate, trackId));
}

void Method_Release_Track(const FunctionCallbackInfo<Value>& args){
	if (manager != nullptr) {
		manager->releaseTrack(trackId);
		unselectTrack();
	}
}

void Method_Clear_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr) {
		track->clear();
	}
}

void Method_Play_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr) {
		track->play();
	}
}

void Method_Pause_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr) {
		track->pause();
	}
}

void Method_Resume_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr) {
		track->resume();
	}
}

void Method_Toggle_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr) {
		track->toggle();
	}
}

void Method_Seek_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		track->seek(args[0]->NumberValue());
	}
}

void Method_Time_Track(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double time;
	if (track != nullptr) {
		time = track->getTime();
	} else {
		time = NAN;
	}
	args.GetReturnValue().Set(Number::New(isolate, time));
}

void Method_Buf_Time_Track(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double time;
	if (track != nullptr) {
		time = track->getBufTime();
	} else {
		time = NAN;
	}
	args.GetReturnValue().Set(Number::New(isolate, time));
}

void Method_Length_Track(const FunctionCallbackInfo<Value>& args){
	Isolate* isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double len;
	if (track != nullptr) {
		len = track->getLength();
	} else {
		len = 0.0;
	}
	args.GetReturnValue().Set(Number::New(isolate, len));
}

void Method_SetLength_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		track->setLength(args[0]->NumberValue());
	}
}

void Method_Volume_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		track->setVolume(static_cast<float>(args[0]->NumberValue() / 100.0));
	}
}

void Method_Speed_Track(const FunctionCallbackInfo<Value>& args){
	if (track != nullptr && args.Length() == 1 && args[0]->IsNumber()) {
		track->setSpeed(static_cast<float>(args[0]->NumberValue()));
	}
}

void Method_Get_Latency(const FunctionCallbackInfo<Value> & args) {
	if (manager == nullptr) {
		return;
	}
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double latency = manager->getDSPDelay();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
	latency += 30;
#endif
	args.GetReturnValue().Set(Number::New(isolate, latency));
}

void Method_Get_Buf_Length(const FunctionCallbackInfo<Value> & args) {
	if (manager == nullptr) {
		return;
	}
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	double bufLength = manager->getDSPDelay();
	args.GetReturnValue().Set(Number::New(isolate, bufLength));
}

void Method_Volume_Cell(const FunctionCallbackInfo<Value> & args) {
	if (track != nullptr && cellId >= 0 && args.Length() == 1 && args[0]->IsNumber()) {
		track->setCellVolume(cellId, static_cast<float>(args[0]->NumberValue() / 100.0));
	}
}

void Method_Is_Playing_Track(const FunctionCallbackInfo<Value> & args) {
	Isolate * isolate = Isolate::GetCurrent();
	HandleScope scope(isolate);
	bool playing = track && track->isPlaying();
	args.GetReturnValue().Set(Boolean::New(isolate, playing));
}

/**
使用说明:
fmod全局单例, 引入时调一次init, 此后不能再调init
不需要使用, 调destory进行销毁
load时传入绝对路径, 并用返回的id 进行后续操作

需要以60fps的频率调update
*/

void init(Handle<Object> target) {
	initTimeUtil();

	NODE_SET_METHOD(target, "init", Method_Init);
	NODE_SET_METHOD(target, "destroy", Method_Destroy);
	NODE_SET_METHOD(target, "update", Method_Update);

	NODE_SET_METHOD(target, "getLatency", Method_Get_Latency);
	NODE_SET_METHOD(target, "getBufLength", Method_Get_Buf_Length);

	NODE_SET_METHOD(target, "selectAudio", Method_Select_Audio);
	NODE_SET_METHOD(target, "loadAudio", Method_Load_Audio);
	NODE_SET_METHOD(target, "unloadAudio", Method_Unload_Audio);
	NODE_SET_METHOD(target, "playAudio", Method_Play_Audio);
	NODE_SET_METHOD(target, "pauseAudio", Method_Pause_Audio);
	NODE_SET_METHOD(target, "stopAudio", Method_Stop_Audio);
	NODE_SET_METHOD(target, "toggleAudio", Method_Toggle_Audio);
	NODE_SET_METHOD(target, "volumeAudio", Method_Volume_Audio);
	NODE_SET_METHOD(target, "seekAudio", Method_Seek_Audio);
	NODE_SET_METHOD(target, "timeAudio", Method_Time_Audio);
	NODE_SET_METHOD(target, "lengthAudio", Method_Length_Audio);

	NODE_SET_METHOD(target, "selectCell", Method_Select_Cell);
	NODE_SET_METHOD(target, "addCell", Method_Add_Cell),
	NODE_SET_METHOD(target, "removeCell", Method_Remove_Cell),
	NODE_SET_METHOD(target, "offsetCell", Method_Offset_Cell),
	NODE_SET_METHOD(target, "volumeCell", Method_Volume_Cell),

	NODE_SET_METHOD(target, "selectTrack", Method_Select_Track);
	NODE_SET_METHOD(target, "createTrack", Method_Create_Track);
	NODE_SET_METHOD(target, "releaseTrack", Method_Release_Track);
	NODE_SET_METHOD(target, "clearTrack", Method_Clear_Track);
	NODE_SET_METHOD(target, "playTrack", Method_Play_Track);
	NODE_SET_METHOD(target, "pauseTrack", Method_Pause_Track);
	NODE_SET_METHOD(target, "resumeTrack", Method_Resume_Track);
	NODE_SET_METHOD(target, "toggleTrack", Method_Toggle_Track);
	NODE_SET_METHOD(target, "isPlayingTrack", Method_Is_Playing_Track);
	NODE_SET_METHOD(target, "volumeTrack", Method_Volume_Track);
	NODE_SET_METHOD(target, "seekTrack", Method_Seek_Track);
	NODE_SET_METHOD(target, "timeTrack", Method_Time_Track);
	NODE_SET_METHOD(target, "bufTimeTrack", Method_Buf_Time_Track);
	NODE_SET_METHOD(target, "lengthTrack", Method_Length_Track);
	NODE_SET_METHOD(target, "setLenTrack", Method_SetLength_Track);
	NODE_SET_METHOD(target, "speedTrack", Method_Speed_Track);

}

NODE_MODULE(binding, init);




